#ifndef T_HTTPREPLY_H
#define T_HTTPREPLY_H

#include <QtGui>
#include <QtNetwork>
#include "nbr.h"

extern QMap<QUrl, QString> s_redirect_info;
extern QMap<QUrl, QString> s_url_info;
extern QMap<QString, qint64> s_size_info;

extern QUrl s_unify_url(QUrl a_url);


class T_HttpReply : public QNetworkAccessManager
{
	Q_OBJECT
public:
	explicit T_HttpReply(
			QNetworkAccessManager::Operation op,
			QNetworkRequest request,
			QIODevice * outgoingData = 0,
			QObject * parent = 0)
				: QNetworkAccessManager(parent)
				, m_op(op)
				, m_request(request)
				, m_outgoingData(outgoingData)
				, m_url(m_request.url())
                , m_netReply(0)
				, m_readyRead(false)
                , m_response(0)
	{
		QNetworkAccessManager::setCookieJar(0);
	}
	~T_HttpReply() {}

	QNetworkAccessManager::Operation m_op;
	QNetworkRequest m_request;
	QIODevice * m_outgoingData;
	QUrl m_url;
    QNetworkReply *m_netReply;
    bool m_readyRead;
    QIODevice * m_response;

	// abort()
    virtual void abort() = 0; /**/ //TODO: not used
    // errorString()
    virtual QString errorString() const
    {
        if(m_netReply) return m_netReply->errorString();
        return "";
    }

	// raw headers:
	virtual bool hasRawHeader(const QByteArray &headerName) const = 0;
	virtual QList<QByteArray> rawHeaderList() const = 0;
	virtual QByteArray rawHeader(const QByteArray &headerName) const = 0;

	// attributes
	virtual QVariant attribute(QNetworkRequest::Attribute code) const = 0;

	// reading
	virtual qint64 bytesAvailable() = 0;
    virtual QByteArray readInternal(qint64 a_max) = 0;
    QByteArray read(qint64 a_max)
    {
        QByteArray v_bytes = this->readInternal(a_max);
        if(m_response) m_response->write(v_bytes);
        return v_bytes;
    }

    // SSL
    virtual void ignoreSslErrors()
    {
        if(m_netReply) m_netReply->ignoreSslErrors();
    }
    virtual void ignoreSslErrors(const QList<QSslError> & errors)
    {
        if(m_netReply) m_netReply->ignoreSslErrors(errors);
    }
    virtual QSslConfiguration sslConfiguration() const
    {
        if(m_netReply) return m_netReply->sslConfiguration();
        return QSslConfiguration::defaultConfiguration();
    }
    virtual void setSslConfiguration(const QSslConfiguration & config)
    {
        if(m_netReply) m_netReply->setSslConfiguration(config);
    }

Q_SIGNALS:
	void authenticationRequired(QAuthenticator *authenticator); // from QNetworkAccessManager
	void readyRead();
	void metaDataChanged();
	void finished();
	void error(QNetworkReply::NetworkError code);
	void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
	void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
    void sslErrors(const QList<QSslError> & errors);

protected Q_SLOTS:
	// emit handlers
	virtual void emit_namAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
	{
		Q_UNUSED(reply);
		emit authenticationRequired(authenticator);
	}
	virtual void emit_readyRead()
	{
		m_readyRead = true;
		emit readyRead();
	}
	virtual void emit_metaDataChanged() { emit metaDataChanged(); }
	virtual void emit_finished() { emit finished(); }
	virtual void emit_error(QNetworkReply::NetworkError code) { emit error(code); }
	virtual void emit_uploadProgress(qint64 bytesSent, qint64 bytesTotal)
	{
		emit uploadProgress(bytesSent, bytesTotal);
	}
	virtual void emit_downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
	{
		emit downloadProgress(bytesReceived, bytesTotal);
	}
#ifndef QT_NO_OPENSSL
    virtual void emit_sslErrors(const QList<QSslError> & errors)
    {
        emit sslErrors(errors);
    }
#endif
protected:
    virtual void p_connectQNetworkReply(QNetworkReply * a_netReply)
    {
        //NAM::authenticationRequired()
        connect(dynamic_cast<QNetworkAccessManager *>(this),
                SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
                SLOT(emit_namAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
        //readyRead()
        connect(a_netReply, SIGNAL(readyRead()), SLOT(emit_readyRead()));
        //metaDataChanged()
        connect(a_netReply, SIGNAL(metaDataChanged()), SLOT(emit_metaDataChanged()));
        //finished()
        connect(a_netReply, SIGNAL(finished()), SLOT(emit_finished()));
        //error()
        connect(a_netReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(emit_error(QNetworkReply::NetworkError)));
        //uploadProgress()
        connect(a_netReply, SIGNAL(uploadProgress(qint64,qint64)), SLOT(emit_uploadProgress(qint64,qint64)));
        //downloadProgress()
        connect(a_netReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(emit_downloadProgress(qint64,qint64)));
#ifndef QT_NO_OPENSSL
        connect(a_netReply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(emit_sslErrors(QList<QSslError>)));
#endif
    }
};

class T_HttpReply_1 : public T_HttpReply
{
	Q_OBJECT
public:
    ////QNetworkReply *m_netReply;
    explicit T_HttpReply_1(
			QNetworkAccessManager::Operation op,
			QNetworkRequest request,
			QIODevice * outgoingData = 0,
			QObject * parent = 0);
	// abort()
	virtual void abort();
	// raw headers:
	virtual bool hasRawHeader(const QByteArray &headerName) const;
	virtual QList<QByteArray> rawHeaderList() const;
	virtual QByteArray rawHeader(const QByteArray &headerName) const;
	// attributes
	virtual QVariant attribute(QNetworkRequest::Attribute code) const;
	// reading
	virtual qint64 bytesAvailable();
    virtual QByteArray readInternal(qint64 a_max);
};

class T_HttpReply_2 : public T_HttpReply_1
{
	Q_OBJECT
public:
	QString m_video_id;
    explicit T_HttpReply_2(
			QNetworkAccessManager::Operation op,
			QNetworkRequest request,
			QIODevice * outgoingData = 0,
			QObject * parent = 0);
    ~T_HttpReply_2();
	// emit handlers
	virtual void emit_finished();
	// private functions
	void p_parseStreamMap(QString a_id, QByteArray a_bytes);
};

class T_HttpReply_3 : public T_HttpReply
{
public:
    QNetworkReply *m_headReply;
    ////QNetworkReply *m_netReply;
    ////bool m_metaDataChanged;
    qint64 m_readCounter;
    QString m_cache_file_name;
    QFile m_cache_file;
    qint64 m_cache_offset;
    QTimer m_timer;
    T_HttpReply_3(
            QNetworkAccessManager::Operation op,
            QNetworkRequest request,
            QIODevice * outgoingData = 0,
            QObject * parent = 0);
    // abort()
    virtual void abort();
    // raw headers:
    virtual bool hasRawHeader(const QByteArray &headerName) const;
    virtual QList<QByteArray> rawHeaderList() const;
    virtual QByteArray rawHeader(const QByteArray &headerName) const;
    // attributes
    virtual QVariant attribute(QNetworkRequest::Attribute code) const;
    // reading
    virtual qint64 bytesAvailable();
    virtual QByteArray readInternal(qint64 a_max);
    //private functions
    QString p_url_cache_file_name();
};

#endif // T_HTTPREPLY_H
